/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-11-27     zylx         first version
 */

 #include <rtthread.h>
 #include <rtdevice.h>
 #include <dfs_fs.h>
 #include <fal.h>
 #include <drv_gpio.h>
 #include <drv_spi.h>
 #include <hpm_spi_drv.h>
 #include <board.h>

 #ifdef RT_USING_QSPI

#define DRV_DEBUG
#define LOG_TAG              "drv.qspi"

 #if defined(BSP_USING_QSPI)


#define DMA_SIZE 8192
#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t trx_buffer[DMA_SIZE];


struct rt_completion qspi_cpt;
struct rt_spi_bus qspi_bus;


static void qspi_init(void);
static void quad_spi_format_init(void);


static void qspi_send_cmd(struct rt_qspi_message *message, spi_control_config_t *control_config)
{
    const rt_uint8_t *send_buf = message->parent.send_buf;
    rt_uint8_t *recv_buf = message->parent.recv_buf;
    rt_int32_t message_length = message->parent.length;

    control_config->master_config.cmd_enable =(message->instruction.qspi_lines!=0)? true:false;
    control_config->master_config.addr_enable =(message->address.qspi_lines!=0)? true:false;
    control_config->master_config.addr_phase_fmt = (message->address.qspi_lines==0)? spi_address_phase_format_single_io_mode:
                                                (message->address.qspi_lines==1)? spi_address_phase_format_single_io_mode: spi_address_phase_format_dualquad_io_mode;
    // control_config->common_config.trans_mode = spi_trans_write_read_together; /*这个参数在传输的时候设置, 要根据情况设置*/
    control_config->common_config.data_phase_fmt = (message->qspi_data_lines==0)? spi_single_io_mode:
                                                  (message->qspi_data_lines==1)? spi_single_io_mode:
                                                  (message->qspi_data_lines==2)? spi_dual_io_mode: spi_quad_io_mode;
    
    control_config->common_config.dummy_cnt = (message->dummy_cycles==0)? spi_dummy_count_1:
                                            (message->dummy_cycles==8)? spi_dummy_count_1: /*this gives success result*/
                                            spi_dummy_count_1; 


    // rt_kprintf("message->instruction.qspi_lines=%d\n", message->instruction.qspi_lines);
    // rt_kprintf("message->address.qspi_lines=%d\n", message->address.qspi_lines);
    // rt_kprintf("message->qspi_data_lines=%d\n", message->qspi_data_lines);
    // rt_kprintf("message->dummy_cycles=%d\n", message->dummy_cycles);
    // rt_kprintf("message->parent.length=%d\n", message->parent.length);
    // rt_kprintf("message->parent.send_buf=%p\n", message->parent.send_buf);
    // rt_kprintf("message->parent.recv_buf=%p\n", message->parent.recv_buf);
    // rt_kprintf("control_config->master_config.cmd_enable=%d\n", control_config->master_config.cmd_enable);
    // rt_kprintf("control_config->master_config.addr_enable=%d\n", control_config->master_config.addr_enable);
    // rt_kprintf("control_config->master_config.addr_phase_fmt=%d\n", control_config->master_config.addr_phase_fmt);
    // rt_kprintf("control_config->common_config.trans_mode=%d\n", control_config->common_config.trans_mode);
    // rt_kprintf("control_config->common_config.data_phase_fmt=%d\n", control_config->common_config.data_phase_fmt);
    // rt_kprintf("control_config->common_config.dummy_cnt=%d\n", control_config->common_config.dummy_cnt);

}


static void qspi_send_no_cmd(spi_control_config_t *control_config)
{
    control_config->master_config.cmd_enable =false;
    control_config->master_config.addr_enable =false;    
    control_config->common_config.dummy_cnt = spi_dummy_count_1;
}

static rt_ssize_t qspixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    /*说明，存在的问题：hpm qspi的xfer length最大只能是512字节, 这导致目前的函数mount fs失败，因为df执行存在传输4096 bytes的情况。
    必须要修改这个qspixfer函数，让它能够分包传输才可以。
    */
    #define DMA_MIN_SIZE 100000000
    #define MAX_PACKET_SIZE 512

    rt_ssize_t result = 0;
    struct rt_qspi_message *qspi_message = (struct rt_qspi_message *)message;

    rt_uint8_t *send_buf = (rt_uint8_t *)message->send_buf;
    rt_uint8_t *recv_buf = message->recv_buf;
    rt_int32_t message_length = message->length;

    spi_control_config_t control_config={0};
    hpm_stat_t stat;

    uint32_t first_packet = true;

    
    cs_ctrl_callback_t cs_pin_control = (cs_ctrl_callback_t) device->parent.user_data;
    if ((cs_pin_control != NULL) && message->cs_take)
    {
        cs_pin_control(SPI_CS_TAKE);
    }

    qspi_send_cmd(qspi_message, &control_config);
    
    if(message_length < DMA_MIN_SIZE) { /*small length, use polling*/ 
        if (send_buf && message_length > 0)
        {
            control_config.common_config.trans_mode = (qspi_message->dummy_cycles)? spi_trans_dummy_write: spi_trans_write_only;
            
            /* 分包处理 */
            rt_int32_t remaining = message_length;
            rt_uint8_t *current_send_buf = send_buf;
            
            while (remaining > 0)
            {
                // rt_kprintf("remaining=%d\n", remaining);
                if(first_packet)
                {
                    first_packet = false;
                }
                else{
                    // rt_kprintf("qspi_send_no_cmd=%p\n");
                    qspi_send_no_cmd(&control_config);
                }

                rt_int32_t chunk_size = (remaining > MAX_PACKET_SIZE) ? MAX_PACKET_SIZE : remaining;
                // rt_kprintf("chunk_size=%d\n", chunk_size);
                
                RT_ASSERT(spi_transfer(BOARD_APP_SPI_BASE, &control_config, &qspi_message->instruction.content, &qspi_message->address.content, 
                    current_send_buf, chunk_size, NULL, 0)==status_success);
                
                remaining -= chunk_size;
                current_send_buf += chunk_size;
                result += chunk_size;
            }
        }
        else if (recv_buf && message_length > 0)
        {
            control_config.common_config.trans_mode = (qspi_message->dummy_cycles)? spi_trans_dummy_read: spi_trans_read_only;
            
            /* 分包处理 */
            rt_int32_t remaining = message_length;
            rt_uint8_t *current_recv_buf = recv_buf;
            
            while (remaining > 0)
            {
                // rt_kprintf("remaining=%d\n", remaining);
                if(first_packet)
                {
                    first_packet = false;
                }
                else{
                    // rt_kprintf("qspi_send_no_cmd=%p\n");
                    qspi_send_no_cmd(&control_config);
                }

                rt_int32_t chunk_size = (remaining > MAX_PACKET_SIZE) ? MAX_PACKET_SIZE : remaining;
                // rt_kprintf("chunk_size=%d\n", chunk_size);
                
                RT_ASSERT(spi_transfer(BOARD_APP_SPI_BASE, &control_config, &qspi_message->instruction.content, &qspi_message->address.content, 
                    NULL, 0, current_recv_buf, chunk_size)==status_success);
                
                remaining -= chunk_size;
                current_recv_buf += chunk_size;
                result += chunk_size;
            }
        }
        else{
            control_config.common_config.trans_mode = spi_trans_no_data;
            RT_ASSERT(spi_transfer(BOARD_APP_SPI_BASE, &control_config, &qspi_message->instruction.content, &qspi_message->address.content, 
                NULL, 0, NULL, 0)==status_success);
            result =1;    
        }
    }
    else {/*large length, use dma*/
        // RT_ASSERT(message_length <= DMA_SIZE); /*now only support dma length < 8192 bytes*/
        // TODO
        result = 0;
    }

__exit:
    if ((cs_pin_control != NULL) && message->cs_release)
    {
        cs_pin_control(SPI_CS_RELEASE);
    }

    return result; 
}

static rt_err_t qspi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
{
    qspi_init();
    quad_spi_format_init();

    return RT_EOK;
}

static const struct rt_spi_ops stm32_qspi_ops =
{
    .configure = qspi_configure,
    .xfer = qspixfer,
};



rt_err_t rt_hw_qspi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin, rt_uint8_t data_line_width, void (*enter_qspi_mode)(), void (*exit_qspi_mode)(), cs_ctrl_callback_t callback)
{
    struct rt_qspi_device *qspi_device = RT_NULL;
    rt_err_t result = RT_EOK;

    RT_ASSERT(bus_name != RT_NULL);
    RT_ASSERT(device_name != RT_NULL);
    RT_ASSERT(data_line_width == 1 || data_line_width == 2 || data_line_width == 4);

    qspi_device = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device));
    RT_ASSERT(qspi_device != RT_NULL);

    qspi_device->enter_qspi_mode = enter_qspi_mode;
    qspi_device->exit_qspi_mode = exit_qspi_mode;
    qspi_device->config.qspi_dl_width = data_line_width;

    result = rt_spi_bus_attach_device(&qspi_device->parent, device_name, bus_name, (void*)callback);
    return  result;
}



// void HAL_QSPI_TxCpltCallback(QSPI_HandleTypeDef *hqspi)
// {
//     rt_completion_done(&qspi_cpt);
// }

// void HAL_QSPI_RxCpltCallback(QSPI_HandleTypeDef *hqspi)
// {
//     rt_completion_done(&qspi_cpt);
// }



static int rt_hw_qspi_bus_init(void)
{
    rt_err_t result;

    rt_completion_init(&qspi_cpt);
    result = rt_qspi_bus_register(&qspi_bus, "qspi2", &stm32_qspi_ops);
    RT_ASSERT(result == RT_EOK);

    return result;
}
INIT_BOARD_EXPORT(rt_hw_qspi_bus_init);










static void qspi_init(void)
{
    board_init_spi_clock(BOARD_APP_SPI_BASE);
    // board_init_spi_pins(BOARD_APP_SPI_BASE); /*这个函数使用hard-cs*/
    board_init_spi_pins_with_gpio_as_cs(BOARD_APP_SPI_BASE); /*这个函数使用soft-cs*/
    HPM_IOC->PAD[IOC_PAD_PE26].FUNC_CTL = IOC_PE26_FUNC_CTL_SPI2_DAT3;
    HPM_IOC->PAD[IOC_PAD_PE29].FUNC_CTL = IOC_PE29_FUNC_CTL_SPI2_DAT2;
}

static void quad_spi_format_init(void)
{
    spi_format_config_t format_config= {0};
    format_config.master_config.addr_len_in_bytes = 3; /*3bytes address*/
    format_config.common_config.data_len_in_bits = BOARD_APP_SPI_DATA_LEN_IN_BITS ;
    format_config.common_config.mode = spi_master_mode;
    format_config.common_config.cpol = spi_sclk_low_idle;
    format_config.common_config.cpha = spi_sclk_sampling_odd_clk_edges;
    format_config.common_config.data_merge = false;
    spi_format_init(BOARD_APP_SPI_BASE, &format_config);
}


#endif /* BSP_USING_QSPI */
#endif /* RT_USING_QSPI */